package qy.qyalgotrader.utils;

import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.ListOrderedMap;

import com.google.common.collect.ArrayTable;
import com.google.common.collect.Table;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;

import qy.jalgotrade.bar.Bar;
import qy.jalgotrade.Initializer;

/**
 * @author c-geo
 *
 */
@SuppressWarnings("unchecked")
public final class Constants {

	/**
	 * 资产类型: 可对历史 / 实时行情字段进行分组处理:
	 * 
	 * @author qy
	 *
	 */
	public enum AssetClass {

		SSE_SZSE, // 沪深证券交易所
		CN_FUTURES // 中国期货
	}

	/**
	 * 慢速 Replay 时, 快进倍数:
	 */
	public enum ReplayMultiples {
		X1(1), X2(2), X3(3), X5(5),
		X10(2), X20(20), X30(30), X50(50),
		X100(100), X200(200), X300(300), X500(500),
		X1000(1000);

		private final int multiple;

		ReplayMultiples(int multiple) {

			this.multiple = multiple;
		}

		/**
		 *
		 * @return multiple
		 */
		public int getMultiple() {

			return multiple;
		}
	}

	/**
	 * 
	 * @author c-geo
	 *
	 */
	public static class ArrayTableJsonDeserializer implements JsonDeserializer<ArrayTable<String, String, Object>> {

		/*
		 * (non-Javadoc)
		 * @see com.google.gson.JsonDeserializer#deserialize(com.google.gson.JsonElement, java.lang.reflect.Type, com.google.gson.JsonDeserializationContext)
		 */
		@Override
		public ArrayTable<String, String, Object> deserialize(JsonElement json, Type typeOfT,
		        JsonDeserializationContext context) throws JsonParseException {

			if (!json.isJsonArray()) {
				throw new JsonParseException("Top json element is NOT a JsonArray!");
			}

			JsonArray rows = json.getAsJsonArray();
			int rowNum = rows.size();
			if (rowNum == 0) {
				return ArrayTable.create(Collections.emptyList(), Collections.emptyList());
			}

			JsonElement firstRow = rows.get(0);
			if (!firstRow.isJsonObject()) {
				throw new JsonParseException("Json element[0] is NOT a JsonObject!");
			}
			if (firstRow.getAsJsonObject().size() != 1) {
				throw new JsonParseException("Json element[0] is NOT formed as {rowKey: rowValues}");
			}

			String rowKey = (String) firstRow.getAsJsonObject().keySet().toArray()[0];
			JsonElement firstRowValues = firstRow.getAsJsonObject().get(rowKey);
			if (!firstRowValues.isJsonObject()) {
				throw new JsonParseException("Json element[0] is NOT formed as <rowKey, rowValues>");
			}

			// build colKeys:
			ArrayList<String> colKeys = new ArrayList<>();
			for (Map.Entry<String, JsonElement> e : firstRowValues.getAsJsonObject().entrySet()) {
				colKeys.add(e.getKey());
			}
			// build rowKeys:
			ArrayList<String> rowKeys = new ArrayList<>();
			for (JsonElement r : rows) {
				rowKey = (String) r.getAsJsonObject().keySet().toArray()[0];
				rowKeys.add(rowKey);
			}
			// set Table cells value:
			ArrayTable<String, String, Object> ret = ArrayTable.create(rowKeys, colKeys);
			for (int i = 0; i < rows.size(); i++) {
				JsonObject rowValues = rows.get(i).getAsJsonObject().getAsJsonObject(rowKeys.get(i));
				for (Map.Entry<String, JsonElement> e : rowValues.entrySet()) {
					if (e.getValue().isJsonNull()) {
						// json type null:
						ret.put(rowKeys.get(i), e.getKey(), null);
					} else if (e.getValue().isJsonPrimitive()) {
						// json primitives:
						JsonPrimitive jp = e.getValue().getAsJsonPrimitive();
						if (jp.isBoolean()) {
							ret.put(rowKeys.get(i), e.getKey(), Boolean.parseBoolean(jp.getAsString()));
						} else if (jp.isString()) {
							ret.put(rowKeys.get(i), e.getKey(), jp.getAsString());
						} else { // jp.isNumber() == true
							ret.put(rowKeys.get(i), e.getKey(), Double.parseDouble(jp.getAsString()));
						}
					} else if (e.getValue().isJsonArray()) {
						// deserialize to List<Map<String, Object>>: tradingTimeHist@CN_FUTURES_TRADING_TIME
						JsonArray ja = e.getValue().getAsJsonArray();
						List<Map<String, Object>> ls = new ArrayList<>(ja.size());
						for (int j = 0; j < ja.size(); j++) {
							JsonElement je = ja.get(j);
							if (je.isJsonObject()) {
								Map<String, Object> obj = context.deserialize(je, HashMap.class);
								ls.add(obj);
							} else {
								throw new JsonParseException(String.format(
								        "Only support simple JsonObject within JsonArray (tradingTimeHist@CN_FUTURES_TRADING_TIME), element name: %s",
								        e.getKey()));
							}
						}
						ret.put(rowKeys.get(i), e.getKey(), ls);
					} else {
						throw new JsonParseException(
						        String.format("Unsupported json type (object) for table value: %s", e.getValue()));
					}
				}
			}

			return ret;
		}
	}

	// 应用内日期, 时间交互标准格式:
	/**
	 * YYYY-MM-DD
	 */
	public static final DateTimeFormatter DATE_FORMAT_STD = DateTimeFormatter.ofPattern("yyyy-MM-dd");

	/**
	 * yyyy-MM-dd[ HH:mm:ss.SSSSSS]
	 */
	public static final DateTimeFormatter DATE_FORMAT_STD_OPT = DateTimeFormatter.ofPattern("yyyy-MM-dd[ HH:mm:ss.SSSSSS]");

	/**
	 * HH:MM:SS
	 */
	public static final DateTimeFormatter TIME_FORMAT_STD = DateTimeFormatter.ofPattern("HH:mm:ss");

	/**
	 * HH:MM:SS.sss
	 */
	public static final DateTimeFormatter TIME_FORMAT_MS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");

	/**
	 * HH:MM:SS[.sss]
	 */
	public static final DateTimeFormatter TIME_FORMAT_MS_OPT = DateTimeFormatter.ofPattern("HH:mm:ss[.SSS]");

	/**
	 * HH:MM:SS.ssssss
	 */
	public static final DateTimeFormatter TIME_FORMAT_US = DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSS");

	/**
	 * HH:MM:SS[.ssssss]
	 */
	public static final DateTimeFormatter TIME_FORMAT_US_OPT = DateTimeFormatter.ofPattern("HH:mm:ss[.SSSSSS]");

	/**
	 * YYYY-MM-DD HH:MM:SS
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_STD = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

	/**
	 * YYYY-MM-DD HH:MM:SS.sss
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

	/**
	 * yyyy-MM-dd HH:mm:ss[.SSS]
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_MS_OPT = DateTimeFormatter.
			ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]");

	/**
	 * YYYY-MM-DD HH:MM:SS.ssssss
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_US = DateTimeFormatter
	        .ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");

	/**
	 * YYYY-MM-DD HH:MM:SS[.ssssss]
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_US_OPT = DateTimeFormatter
	        .ofPattern("yyyy-MM-dd HH:mm:ss[.SSSSSS]");

	/**
	 * yyyyMMdd HHmmss
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_YMD_HMS = DateTimeFormatter
            .ofPattern("yyyyMMdd HHmmss");

	/**
	 * yyyyMMddHHmmss
	 */
	public static final DateTimeFormatter DATE_TIME_FORMAT_YMDHMS = DateTimeFormatter
            .ofPattern("yyyyMMddHHmmss");

	/**
	 * YYYYMMDD
	 */
	public static final DateTimeFormatter DATE_FORMAT_YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd");

	/**
	 * HHMMSS
	 */
	public static final DateTimeFormatter TIME_FORMAT_HHMMSS = DateTimeFormatter.ofPattern("HHmmss");

	/**
	 * YYYY
	 */
	public static final DateTimeFormatter DATE_FORMAT_YYYY = DateTimeFormatter.ofPattern("yyyy");

	/**
	 * YYYYMM
	 */
	public static final DateTimeFormatter DATE_FORMAT_YYYYMM = DateTimeFormatter.ofPattern("yyyyMM");

	/**
	 * e.g.: "%Y-%m-%d %H:%M:%S.%f" -&gt; "yyyy-MM-dd HH:mm:ss.SSSSSS"
	 * @param cTimeFmt cTimeFmt
	 * @return DateTimeFormatter
	 */
	public static DateTimeFormatter genCtimeFormat(String cTimeFmt) {

		// @formatter:off
		Map<String, String> cvt = MapUtils.putAll(new HashMap<>(), new Object[][] {
			{"%Y", "yyyy"},
			{"%y", "yy"},
			{"%M", "MM"},
			{"%d", "dd"},
			{"%H", "HH"},
			{"%M", "mm"},
			{"%S", "ss"},
			{"%f", "SSSSSS"},
		});
		// @formatter:on
		StringBuilder jTimeFmt = new StringBuilder(cTimeFmt);
		for (String e : cvt.keySet()) {
			int idx = jTimeFmt.indexOf(e);
			if (idx >= 0) {
				jTimeFmt.replace(idx, idx + e.length(), cvt.get(e));
			}
		}
		return DateTimeFormatter.ofPattern(jTimeFmt.toString());
	}

	/**
	 * 标准日内 Bar 频率: {min1, min3, min5, min15, min30, min60}:
	 */
	public static final List<Bar.Frequency> INTRADAY_FREQS_COMMON = Bar.Frequency.getIntradayFreqsCommon();

	////////////////////////////////////////////////////////////////////////////////////////////////
	// 交易日开盘, 收盘时间常量 (中国 A 股):
	////////////////////////////////////////////////////////////////////////////////////////////////
	// 9:10:00 (特殊初始化时间: 针对上一交易日收盘价, 缺省作为当前交易日的开盘价):
	/**
	 * 初始 Tick 的时间 (09:10:00)
	 */
	public static final LocalTime OPEN_TIME_INIT_TICK = LocalTime.parse("09:10:00", TIME_FORMAT_STD);

	// 9:15:00 ~ 9:20:00: 集合竞价 (可撤单):
	/**
	 * A 股上午盘集合竞价报价开始 (09:15:00)
	 */
	public static final LocalTime OPEN_TIME_AM_CALL_AUCTION_QUOTES = LocalTime.parse("09:15:00", TIME_FORMAT_STD);

	/**
	 * A 股上午盘集合竞价报价开始 (不可撤单) (09:20:00)
	 */
	public static final LocalTime OPEN_TIME_AM_CALL_AUCTION_QUOTES_ONLY = LocalTime.parse("09:20:00", TIME_FORMAT_STD);

	// 9:20:00 ~ 9:25:00: 集合竞价 (不可撤单):
	/**
	 * A 股上午盘开盘集合竞价 (09:25:00)
	 */
	public static final LocalTime OPEN_TIME_AM_CALL_AUCTION = LocalTime.parse("09:25:00", TIME_FORMAT_STD);

	//  9:25:00: 集合竞价结束; 9:30:00 ~ 11:30: 上午盘连续竞价:
	/**
	 * A 股上午盘开盘连续竞价 (09:30:00)
	 */
	public static final LocalTime OPEN_TIME_AM = LocalTime.parse("09:30:00", TIME_FORMAT_STD);

	/**
	 * A 股上午收盘最后一个交易分钟 (11:29:00)
	 */
	public static final LocalTime CLOSE_MINUTE_AM = LocalTime.parse("11:29:00", TIME_FORMAT_STD);

	/**
	 * A 股上午盘收盘 (11:30:00)
	 */
	public static final LocalTime CLOSE_TIME_AM = LocalTime.parse("11:30:00", TIME_FORMAT_STD);

	// 13:00:00 ~ 14:57:00: 下午盘连续竞价 (深交所); 14:57:00 ~ 15:00:00: 下午盘集合竞价 (深交所):
	/**
	 * A 股下午盘开盘 (13:00:00)
	 */
	public static final LocalTime OPEN_TIME_PM = LocalTime.parse("13:00:00", TIME_FORMAT_STD);

	// 15:00:00: 下午盘集合竞价结束 (深交所), 下午盘连续竞价结束 (上交所):
	/**
	 * A 股下午收盘集合竞价前最后一个交易分钟 (深交所) (14:56:00)
	 */
	public static final LocalTime CLOSE_MINUTE_PM_PRE_CALL_AUCTION = LocalTime.parse("14:56:00", TIME_FORMAT_STD);

	/**
	 * A 股下午收盘集合竞价开始 (深交所) (14:57:00)
	 */
	public static final LocalTime CLOSE_MINUTE_PM_CALL_AUCTION = LocalTime.parse("14:57:00", TIME_FORMAT_STD);

	/**
	 * A 股下午收盘最后一个交易分钟 (14:59:00)
	 */
	public static final LocalTime CLOSE_MINUTE_PM = LocalTime.parse("14:59:00", TIME_FORMAT_STD);

	/**
	 * A 股下午盘收盘 (15:00:00)
	 */
	public static final LocalTime CLOSE_TIME_PM = LocalTime.parse("15:00:00", TIME_FORMAT_STD);

	/**
	 * A 股下午盘收盘集合竞价结束 (深交所) (15:01:00)
	 */
	public static final LocalTime CLOSE_TIME_PM_CALL_AUCTION = LocalTime.parse("15:01:00", TIME_FORMAT_STD);

	/**
	 * 上交所收盘集合竞价开始施行日期: 2018-08-20
	 */
	public static final LocalDate SSE_CLOSE_CALL_AUCTION_START = LocalDate.parse("2018-08-20", DATE_FORMAT_STD);

	/**
	 * 深交所收盘集合竞价开始施行日期: 2006-07-01
	 */
	public static final LocalDate SZSE_CLOSE_CALL_AUCTION_START = LocalDate.parse("2006-07-01", DATE_FORMAT_STD);

	// 复盘时间开始 / 截止:
	/**
	 * 复盘时间开始 (16:15:00)
	 */
	public static final LocalTime REPLAY_TIME_START = LocalTime.parse("16:15:00", TIME_FORMAT_STD);

	/**
	 * 复盘时间截止 (次交易日) (08:30:00)
	 */
	public static final LocalTime REPLAY_TIME_END = LocalTime.parse("08:30:00", TIME_FORMAT_STD);

	/**
	 * 交易日临界时间: (19:00:00)
	 */
	public static final LocalTime TRADING_DAY_BORDER = LocalTime.parse("19:00:00", TIME_FORMAT_STD);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// 交易日开盘, 收盘时间常量 (中国期货):
	////////////////////////////////////////////////////////////////////////////////////////////////
	/*
	 * 夜盘集合竞价:
	 * 申报: 20:55:00 ~ 20:59:00
	 * 撮合成交: 20:59:00 ~ 21:00:00
	 * 开盘前 5 分钟为集合竞价, 前 4 分钟申报, 第 5 分钟撮合成交, 即集合竞价 Tick 的规范时间为: OPEN_TIME - min_1
	 */
	/**
	 * 中国期货夜盘开盘集合竞价 (20:59:00)
	 */
	public static final LocalTime OPEN_TIME_CN_FU_NT_CALL_AUCTION = LocalTime.parse("20:59:00", TIME_FORMAT_STD);

	/**
	 * 中国期货收盘 (延迟) (15:16:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_PM_CALL_AUCTION = LocalTime.parse("15:16:00", TIME_FORMAT_STD);

	/*
	 * 夜盘连续竞价:
	 * 21:00:00 ~ 23:00:00: RB, HC, RU, ...
	 * 21:00:00 ~ 23:30:00: J, JM, ZC, I, ...
	 * 21:00:00 ~ (+1) 01:00:00: CU, AL, PB, ZN, NI, ...
	 * 21:00:00 ~ (+1) 02:30:00: AU, AG, SC, ...
	 */
	/**
	 * 中国商品夜盘开盘连续竞价 (21:00:00)
	 */
	public static final LocalTime OPEN_TIME_CN_FU_NT = LocalTime.parse("21:00:00", TIME_FORMAT_STD);

	/**
	 * 中国商品夜盘收盘1: 上期所 (螺纹钢, 热轧卷板, 燃油, 沥青, 橡胶, 纸浆, ...) (23:00:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_1 = LocalTime.parse("23:00:00", TIME_FORMAT_STD);

	/**
	 * 中国商品夜盘收盘2: 郑商所 (23:30:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_2 = LocalTime.parse("23:30:00", TIME_FORMAT_STD);

	/**
	 * 中国商品夜盘收盘3: 上期所 (基本金属) (01:00:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_3 = LocalTime.parse("01:00:00", TIME_FORMAT_STD);

	/**
	 * 中国商品夜盘收盘4: 上期所 (原油, 贵金属) (02:30:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_4 = LocalTime.parse("02:30:00", TIME_FORMAT_STD);

	// 上期所 (夜盘交易上线: 2013-07-05 21:00:00): 21:00:00 ~ (+1) 02:30:00
	/**
	 * {@link #OPEN_TIME_CN_FU_NT}
	 */
	public static final LocalTime OPEN_TIME_CN_FU_NT_SHFE = OPEN_TIME_CN_FU_NT;

	/**
	 * {@link #CLOSE_TIME_CN_FU_NT_4}
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_SHFE = CLOSE_TIME_CN_FU_NT_4;

	// 大商所 (夜盘交易上线: 2014-07-04): 21:00:00 ~ 23:00:00
	/**
	 * {@link #OPEN_TIME_CN_FU_NT}
	 */
	public static final LocalTime OPEN_TIME_CN_FU_NT_DCE = OPEN_TIME_CN_FU_NT;

	/**
	 * {@link #CLOSE_TIME_CN_FU_NT_1}
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_DCE = CLOSE_TIME_CN_FU_NT_1;

	// 郑商所 (夜盘交易上线: 2014-12-12 21:00:00): 21:00:00 ~ 23:30:00
	/**
	 * {@link #OPEN_TIME_CN_FU_NT}
	 */
	public static final LocalTime OPEN_TIME_CN_FU_NT_CZCE = OPEN_TIME_CN_FU_NT;

	/**
	 * {@link #CLOSE_TIME_CN_FU_NT_2}
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_NT_CZCE = CLOSE_TIME_CN_FU_NT_2;

	/**
	 * 中国商品: 无夜盘交易, 集合竞价顺延至下个交易日上午盘 (08:59:00):
	 */
	public static final LocalTime OPEN_TIME_CN_FU_AM_CALL_AUCTION = LocalTime.parse("08:59:00", TIME_FORMAT_STD);

	// 上午盘连续竞价:
	/**
	 * 中国商品上午盘连续竞价 (09:00:00)
	 */
	public static final LocalTime OPEN_TIME_CN_FU_AM = LocalTime.parse("09:00:00", TIME_FORMAT_STD);

	/**
	 * 中国商品上午盘暂停开始 (10:15:00)
	 */
	public static final LocalTime PAUSE_TIME_BEGIN_CN_FU_AM = LocalTime.parse("10:15:00", TIME_FORMAT_STD);

	/**
	 * 中国商品上午盘暂停结束 (10:30:00)
	 */
	public static final LocalTime PAUSE_TIME_END_CN_FU_AM = LocalTime.parse("10:30:00", TIME_FORMAT_STD);

	/**
	 * 中国商品上午盘收盘 (11:30:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_AM = LocalTime.parse("11:30:00", TIME_FORMAT_STD);

	// 下午盘连续竞价
	/**
	 * 中国商品下午盘开盘 (13:30:00)
	 */
	public static final LocalTime OPEN_TIME_CN_FU_PM = LocalTime.parse("13:30:00", TIME_FORMAT_STD);

//	/**
//	 * 中国商品下午盘收盘 (15:00:00)
//	 */
//	public static final LocalTime CLOSE_TIME_CN_FU_PM = LocalTime.parse("15:00:00", TIME_FORMAT_STD);

	/**
	 * 中国期货收盘 (最晚收盘: 国债期货 15:15:00)
	 */
	public static final LocalTime CLOSE_TIME_CN_FU_PM = LocalTime.parse("15:15:00", TIME_FORMAT_STD);

	/**
	 * 夜盘判断标准: 17:30:00
	 */
	public static final LocalTime NIGHT_MKT_TIME_CN_FU = LocalTime.parse("17:30:00", TIME_FORMAT_STD);

	/**
	 * 上午盘判断标准: 08:00:00
	 */
	public static final LocalTime AM_MKT_TIME_CN_FU = LocalTime.parse("08:00:00", TIME_FORMAT_STD);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// 交易日开盘, 收盘时间常量 (股指期货, 国债期货):
	////////////////////////////////////////////////////////////////////////////////////////////////
	// 股指期货:
	/**
	 * 09:29:00
	 */
	public static final LocalTime OPEN_TIME_IF_CALL_AUCTION = LocalTime.parse("09:29:00", TIME_FORMAT_STD);

	/**
	 * 09:30:00
	 */
	public static final LocalTime OPEN_TIME_IF_AM = LocalTime.parse("09:30:00", TIME_FORMAT_STD);

	/**
	 * 11:30:00
	 */
	public static final LocalTime CLOSE_TIME_IF_AM = LocalTime.parse("11:30:00", TIME_FORMAT_STD);

	/**
	 * 13:00:00
	 */
	public static final LocalTime OPEN_TIME_IF_PM = LocalTime.parse("13:00:00", TIME_FORMAT_STD);

	/**
	 * 15:00:00
	 */
	public static final LocalTime CLOSE_TIME_IF_PM = LocalTime.parse("15:00:00", TIME_FORMAT_STD);

	// 国债期货:
	/**
	 * 09:14:00
	 */
	public static final LocalTime OPEN_TIME_TF_CALL_AUCTION = LocalTime.parse("09:14:00", TIME_FORMAT_STD);

	/**
	 * 09:15:00
	 */
	public static final LocalTime OPEN_TIME_TF_AM = LocalTime.parse("09:15:00", TIME_FORMAT_STD);

	/**
	 * 11:30:00
	 */
	public static final LocalTime CLOSE_TIME_TF_AM = LocalTime.parse("11:30:00", TIME_FORMAT_STD);

	/**
	 * 13:00:00
	 */
	public static final LocalTime OPEN_TIME_TF_PM = LocalTime.parse("13:00:00", TIME_FORMAT_STD);

	/**
	 * 15:15:00
	 */
	public static final LocalTime CLOSE_TIME_TF_PM = LocalTime.parse("15:15:00", TIME_FORMAT_STD);

	//2016年1月1日 之前股指期货交易时间:
	/**
	 * 09:14:00
	 */
	public static final LocalTime OPEN_TIME_IF_CALL_AUCTION_OLD = LocalTime.parse("09:14:00", TIME_FORMAT_STD);

	/**
	 * 09:15:00
	 */
	public static final LocalTime OPEN_TIME_IF_AM_OLD = LocalTime.parse("09:15:00", TIME_FORMAT_STD);

	//合约交割日下午盘收盘时间为 15:00:00:
	/**
	 * 15:00:00
	 */
	public static final LocalTime CLOSE_TIME_IF_PM_OLD_LAST_TD = LocalTime.parse("15:00:00", TIME_FORMAT_STD);

	/**
	 * "15:15:00"
	 */
	public static final LocalTime CLOSE_TIME_IF_PM_OLD = LocalTime.parse("15:15:00", TIME_FORMAT_STD);

	// 四大期货交易所成立日期:
	/**
	 * 1990-10-12
	 */
	public static final LocalDate FOUND_DATE_CZCE = LocalDate.parse("1990-10-12", DATE_FORMAT_STD);

	/**
	 * 1990-11-26
	 */
	public static final LocalDate FOUND_DATE_SHFE = LocalDate.parse("1990-11-26", DATE_FORMAT_STD);

	/**
	 * 1993-02-28
	 */
	public static final LocalDate FOUND_DATE_DCE = LocalDate.parse("1993-02-28", DATE_FORMAT_STD);

	/**
	 * 2006-09-08
	 */
	public static final LocalDate FOUND_DATE_CFFEX = LocalDate.parse("2006-09-08", DATE_FORMAT_STD);

	/**
	 * IF 合约 (中金所首个品种) 上市日期: 2010-04-16 (最早历史中国期货合约日期: 1995-04-17):
	 */
	public static final LocalDate CFFEX_PIONEER_CONTRACT_DATE = LocalDate.parse("2010-04-16", DATE_FORMAT_STD);

	// 中国 A 股价格涨跌幅限制:
	public static final double PRICE_LMT_CN_STOCK_A_UP = 1.1;

	public static final double PRICE_LMT_CN_STOCK_A_DOWN = 0.9;

	// 中国 A 股报价有效位数:
	public static final int PRICE_SIG_DIGITS_CN_STOCK_A = 2;

	// Wind Quant API 指标元数据 Key 常量清单 (公用):
	public static final String WIND_API_META_REQ = "req";

	public static final String WIND_API_META_RESP = "resp";

	public static final String WIND_API_META_CMMT = "cmmt";

	// Wind Quant API 指标元数据 Key 常量清单 (WST):
	// Wind Quant API 指标元数据 Key 常量清单 (WSQ):

	/**
	 * <pre>
	 * wsi() 指标元数据: {请求指标名称: {请求指标名称, 响应指标名称, 指标中文名称}}:
	 * constants.wfnmm.json
	 * </pre>
	 */
	public static final Table<String, String, Object> WIND_FIELD_NAME_MAPPING_MIN_1;

	/**
	 * <pre>
	 * wst() 指标元数据: {请求指标名称: {请求指标名称, 响应指标名称, 指标中文名称, 相对应的实时行情数据字段, 相对应的下一交易日实时行情数据字段}}:
	 * WindApi wst() 获取历史 Tick 行情时使用:
	 * constants.wfnmw.json
	 * </pre>
	 */
	public static final Table<String, String, Object> WIND_FIELD_NAME_MAPPING_WST;

	/**
	 * <pre>
	 * wsq() 指标元数据:
	 *     {请求指标名称: {请求指标名称, 响应指标名称, 分时数据采样计算模式, 指标注释, 相对应的历史行情数据字段, 相对应的历史 Tick 行情数据字段}}:
	 * constants.wfnmrq.json
	 * </pre>
	 */
	public static final Table<String, String, Object> WIND_FIELD_NAME_MAPPING_RT_QUOTES;

	/**
	 * <pre>
	 * wst() 指标元数据 (中国商品): {请求指标名称: {请求指标名称, 响应指标名称, 指标中文名称, 相对应的实时行情数据字段}}:
	 * CTP, WindApi 等实时行情订阅中使用:
	 * constants.wfnmwfc.json
	 * </pre>
	 */
	public static final Table<String, String, Object> WIND_FIELD_NAME_MAPPING_WST_FUTURES_CN;

	/**
	 * <pre>
	 * wsq() 指标元数据 (中国商品):
	 *     {请求指标名称: {请求指标名称, 响应指标名称, 指标注释, 相对应的历史行情数据字段, 相对应的历史 Tick 行情数据字段}}:
	 * constants.wfnmrqfc.json
	 * </pre>
	 */
	public static final Table<String, String, Object> WIND_FIELD_NAME_MAPPING_RT_QUOTES_FUTURES_CN;

	/**
	 * 获取历史 1min 行情的默认指标: 开盘价, 收盘价, 最高价, 最低价, 成交量, 成交额, 涨跌, 涨跌幅, 持仓量:
	 */
	public static final List<String> DEFAULT_FIELDS_MIN_1;

	/**
	 * 获取历史 Tick 行情的默认指标: 前收盘价, 开盘价, 最高价, 最低价, 最新价, 成交量, 成交额, 量比, 持仓量, ...:
	 */
	public static final List<String> DEFAULT_FIELDS_WST;

	/**
	 * 订阅实时行情的默认指标: 日期, 时间, 前收, 今开, 最高, 最低, 现价, 均价, 最新成交价, 成交量, 成交额, 换手率, 量比, 持仓量, 涨跌幅, 振幅, 停牌标志:
	 */
	public static final List<String> DEFAULT_FIELDS_RT_QUOTES;

	// 常用指标名称常量 (tushare / 通用):
	public static final String DATE_TIME = "datetime";

	public static final String DATE = "date";

	public static final String TIME = "time";

	public static final String PRE_CLOSE = "pre_close";

	public static final String PRICE = "price";

	public static final String CHANGE = "change";

	public static final String PCT_CHANGE = "pctchange";

	public static final String VOLUME = "volume";

	public static final String AMOUNT = "amount";

	public static final String VWAP = "vwap";

	/**
	 * 日内分时 VWAP
	 */
	public static final String VWAP_INTRADAY = "vwap_intraday";

	public static final String WIND_CODE = "windcode";

	public static final String RT_QOUTES_DATEATE = "rt_date";

	public static final String RT_QOUTES_TIME = "rt_time";

	// pyalgotrade.bar.Bar:
	public static final String BAR_FIELD_OPEN = "open";

	public static final String BAR_FIELD_HIGH = "high";

	public static final String BAR_FIELD_LOW = "low";

	public static final String BAR_FIELD_CLOSE = "close";

	/**
	 * 成交量:
	 */
	public static final String BAR_FIELD_VOLUME = "volume";

	/**
	 * 成交额:
	 */
	public static final String BAR_FIELD_AMOUNT = "amount";

	/**
	 * Bar vwap:
	 */
	public static final String BAR_FIELD_VWAP = VWAP;

	/**
	 * 日内 vwap:
	 */
	public static final String BAR_FIELD_VWAP_INTRADAY = VWAP_INTRADAY;

	/**
	 * 日内 涨幅:
	 */
	public static final String BAR_FIELD_PCT_CHANGE_INTRADAY = "pctchange_intraday";

	/**
	 * 日内 振幅:
	 */
	public static final String BAR_FIELD_RANGE_INTRADAY = "range_intraday";

	/**
	 * 持仓量:
	 */
	public static final String BAR_FIELD_POSITION = "position";

	// Tushare API 常用常量:
	/**
	 * get_tick_data() 函数 src 参数 (数据源选择: "sn" (新浪), "tt" (腾讯), "nt" (网易); 缺省为 "sn") 的默认值: "tt" (腾讯):
	 */
	public static final String TS_DEFAULT_GET_TICK_DATA_SRC = "tt";

	/**
	 * 分钟 Bars 默认字段: 参考 WIND_FIELD_NAME_MAPPING_MIN_1 (不含 oi (持仓量))
	 */
	public static final List<String> TS_DEFAULT_FIELDS_MIN;

	/**
	 * <pre>
	 * 交易所信息: {交易所缩写: {交易所缩写, windCode 后缀, 交易所中文名称, 交易所中文简称}}:
	 * constants.eiw.json
	 * </pre>
	 */
	public static final Table<String, String, Object> EXCHANGE_INFOS_WIND;

	/**
	 * e.g.: CFE -> CFFEX
	 * 
	 * @param exchSffx
	 * @return
	 */
	public static String exchSffxToName(String exchSffx) {

		for (String e : EXCHANGE_INFOS_WIND.rowKeySet()) {
			if (EXCHANGE_INFOS_WIND.get(e, "exchWindSuffix").equals(exchSffx)) {
				return e;
			}
		}
		throw new IllegalArgumentException(String.format("Unknown exchSuffix: %s", exchSffx));
	}

	/**
	 * 交易所收盘 (交易暂停) last tick 最大延迟时间 (秒):
	 */
	// @formatter:off
	public static Map<String, Integer> LAST_TICK_DELAY_INFOS = MapUtils.putAll(new HashMap<>(), new Object[][] {
		{"SSE", 18},
		{"SZSE", 18},
		{"CFFEX", 18},
		{"SHFE", 18},
		{"DCE", 18},
		{"CZCE", 18}
	});
	// @formatter:on

	/**
	 * last tick 最大延迟时间 (秒) 缺省值:
	 */
	public static final double LAST_TICK_DELAY_SEC_DEFAULT = 18;

	/**
	 * <pre>
	 * 中国期货合约 / 代码对应表:
	 * dict: {
	 *     key: futures-name,
	 *     value: <wind-code(windCode), contract-name(contractName), exchange-id(exchName),
	 *         openTimeNtCa, openTimeNt, closeTimeNt, openTimeAmCa, openTimeAm, pauseTimeAmBegin, pauseTimeAmEnd, closeTimeAm, openTimePm, closeTimePm, tradeUnit, marginRate>
	 *         (夜盘集合竞价开盘, 夜盘开盘, 夜盘收盘, 上午集合竞价开盘, 上午开盘, 上午交易暂停开始, 上午暂停交易结束, 上午收盘, 下午开盘, 下午收盘, 交易每手数量, 保证金比率)
	 * }
	 * 中国期货无夜盘品种列表: qyalgotrader.utils.datautils#is_cn_futures_code_has_night_market()
	 * constants.cfc.json
	 * </pre>
	 */
	public static final Table<String, String, Object> CN_FUTURES_CODES;

	/**
	 * <pre>
	 * 中国期货交易时间变更历史信息 (定义在 qyalgotrader.utils.constantshis 中):
	 * 夜盘首日, 夜盘交易时间变更 指自然日, 实际属于 T+1 交易时间段:
	 * dict: {
	 *     key: futures-name,
	 *     value: &lt;wind-code(windCode), contract-name(contractName), exchange-id(exchName), trading-time-hist[]&gt;;
	 *
	 *     trading-time-hist: &lt;start-date, end-date,
	 *         openTimeNtCa, openTimeNt, closeTimeNt,
	 *         openTimeAmCa, openTimeAm, pauseTimeAmBegin, pauseTimeAmEnd, closeTimeAm,
	 *         openTimePm, closeTimePm,
	 *         tradeUnit, marginRate&gt;
	 *         (开始日期, 结束日期,
	 *                         夜盘集合竞价开盘, 夜盘开盘, 夜盘收盘,
	 *                         上午集合竞价开盘, 上午开盘, 上午交易暂停开始, 上午暂停交易结束, 上午收盘,
	 *                         下午开盘, 下午收盘,
	 *                         交易每手数量, 保证金比率)
	 * }
	 * 
	 * constants.cftt.json
	 * </pre>
	 * 
	 */
	public static final Table<String, String, Object> CN_FUTURES_TRADING_TIME;

	/**
	 * <pre>
	 * 中国期货未上市合约列表: 中金所外汇期货, 国债期货, ...:
	 * constants.cfuf.json
	 * </pre>
	 */
	public static final Table<String, String, Object> CN_FUTURES_UNAVAILABLE_FILTER;

	/**
	 * <pre>
	 * 中国期货流动性较差合约列表:
	 * constants.cfif.json
	 * </pre>
	 */
	public static final Table<String, String, Object> CN_FUTURES_ILLIQUIDITY_FILTER;

	/**
	 * 沪深交易所 A 股 / 基金交易单位: 100 股 (份) / 手:
	 */
	public static final double TRADE_UNIT_SSE_SZSE_DEFAULT = 100.0;

	/**
	 * 沪深交易所指数所属市场类型: 上海, 深圳, 沪深跨市场:
	 * 
	 * @author c-geo
	 *
	 */
	public enum SSE_SZSE_INDEX_MARKET {
		SSE, SZSE, SSE_SZSE
	}

	/**
	 * <pre>
	 * 沪深交易所股票市场指数: (TODO: 指数 tradeUnit 和 marginRate 的使用场景):
	 * constants.sssmi.json
	 * </pre>
	 */
	public static final Table<String, String, Object> SSE_SZSE_STOCK_MKT_I;

	/**
	 * 中信一级行业指数:
	 */
	public static final Map<String, String> CI_INDUS_I;

	/**
	 * 申万一级行业指数:
	 */
	public static final Map<String, String> SI_INDUS_I;

	static {

		GsonBuilder gsb = new GsonBuilder();
		gsb.registerTypeAdapter(ArrayTable.class, new ArrayTableJsonDeserializer());
		Gson gs = gsb.create();

		WIND_FIELD_NAME_MAPPING_MIN_1 = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.wfnmm.json")),
		        ArrayTable.class);
		WIND_FIELD_NAME_MAPPING_WST = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.wfnmw.json")),
		        ArrayTable.class);
		WIND_FIELD_NAME_MAPPING_RT_QUOTES = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.wfnmrq.json")),
		        ArrayTable.class);
		WIND_FIELD_NAME_MAPPING_WST_FUTURES_CN = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.wfnmwfc.json")),
		        ArrayTable.class);
		WIND_FIELD_NAME_MAPPING_RT_QUOTES_FUTURES_CN = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.wfnmrqfc.json")),
		        ArrayTable.class);
		DEFAULT_FIELDS_MIN_1 = new ArrayList<>(WIND_FIELD_NAME_MAPPING_MIN_1.rowKeySet());
		DEFAULT_FIELDS_WST = new ArrayList<>(WIND_FIELD_NAME_MAPPING_WST.rowKeySet());
		DEFAULT_FIELDS_RT_QUOTES = new ArrayList<>(WIND_FIELD_NAME_MAPPING_RT_QUOTES.rowKeySet());
		TS_DEFAULT_FIELDS_MIN = new ArrayList<>();
		for (String e : WIND_FIELD_NAME_MAPPING_MIN_1.rowKeySet()) {
			if (!e.equals("oi")) {
				TS_DEFAULT_FIELDS_MIN.add((String) WIND_FIELD_NAME_MAPPING_MIN_1.get(e, "resp"));
			}
		}
		EXCHANGE_INFOS_WIND = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.eiw.json")),
		        ArrayTable.class);
		CN_FUTURES_CODES = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.cfc.json")),
		        ArrayTable.class);
		// 将 CN_FUTURES_CODES 中的 时间 和 日期 字符串转为 对象:
		for (String rowKey : CN_FUTURES_CODES.rowKeySet()) {
			Map<String, Object> cnFuTdInfos = CN_FUTURES_CODES.row(rowKey);
			// time: openTimeNtCa, openTimeNt, ..., closeTimePm:
			for (String colKey : Arrays.asList("openTimeNtCa", "openTimeNt", "closeTimeNt", "openTimeAmCa",
			        "openTimeAm", "pauseTimeAmBegin", "pauseTimeAmEnd", "closeTimeAm", "openTimePm", "closeTimePm")) {
				if (cnFuTdInfos.get(colKey) != null) {
					CN_FUTURES_CODES.put(rowKey, colKey,
					        LocalTime.parse((String) cnFuTdInfos.get(colKey), TIME_FORMAT_STD));
				}
			}
		}
		CN_FUTURES_TRADING_TIME = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.cftt.json")),
		        ArrayTable.class);
		// 将 list: tradingTimeHist 中的 时间 和 日期 字符串转为 对象:
		for (String rowKey : CN_FUTURES_TRADING_TIME.rowKeySet()) {
			List<Map<String, Object>> aTthList = (List<Map<String, Object>>) CN_FUTURES_TRADING_TIME.get(rowKey,
			        "tradingTimeHist");
			for (int i = 0; i < aTthList.size(); i++) {
				// date: startDate, endDate:
				for (String colKey : Arrays.asList("startDate", "endDate")) {
					if (aTthList.get(i).get(colKey) != null) {
						aTthList.get(i).put(colKey,
						        LocalDate.parse((String) aTthList.get(i).get(colKey), DATE_FORMAT_STD));
					}
				}
				// time: openTimeNtCa, openTimeNt, ..., closeTimePm:
				for (String colKey : Arrays.asList("openTimeNtCa", "openTimeNt", "closeTimeNt", "openTimeAmCa",
				        "openTimeAm", "pauseTimeAmBegin", "pauseTimeAmEnd", "closeTimeAm", "openTimePm",
				        "closeTimePm")) {
					if (aTthList.get(i).get(colKey) != null) {
						aTthList.get(i).put(colKey,
						        LocalTime.parse((String) aTthList.get(i).get(colKey), TIME_FORMAT_STD));
					}
				}
			}
		}
		CN_FUTURES_UNAVAILABLE_FILTER = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.cfuf.json")),
		        ArrayTable.class);
		CN_FUTURES_ILLIQUIDITY_FILTER = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.cfif.json")),
		        ArrayTable.class);
		SSE_SZSE_STOCK_MKT_I = gs.fromJson(
		        new InputStreamReader(Initializer.class.getClassLoader().getResourceAsStream("constants.sssmi.json")),
		        ArrayTable.class);
		// 将 SSE_SZSE_STOCK_MKT_I 中的 日期 等字符串转为 对象:
		for (String rowKey : SSE_SZSE_STOCK_MKT_I.rowKeySet()) {
			Map<String, Object> stockMktI = SSE_SZSE_STOCK_MKT_I.row(rowKey);
			// date: calcStartDate, baseDate, ..., pubDate:
			for (String colKey : Arrays.asList("calcStartDate", "baseDate", "pubDate")) {
				if (stockMktI.get(colKey) != null) {
					SSE_SZSE_STOCK_MKT_I.put(rowKey, colKey,
					        LocalDate.parse((String) stockMktI.get(colKey), DATE_FORMAT_STD));
				}
			}
			SSE_SZSE_STOCK_MKT_I.put(rowKey, "market",
					SSE_SZSE_INDEX_MARKET.valueOf((String) SSE_SZSE_STOCK_MKT_I.get(rowKey, "market")));
		}
		// @formatter:off
		CI_INDUS_I = MapUtils.putAll(new ListOrderedMap<>(), new Object[][] {
			{"CI005001.WI", "石油石化"},
			{"CI005002.WI", "煤炭"},
			{"CI005003.WI", "有色金属"},
			{"CI005004.WI", "电力及公用事业"},
			{"CI005005.WI", "钢铁"},
			{"CI005006.WI", "基础化工"},
			{"CI005007.WI", "建筑"},
			{"CI005008.WI", "建材"},
			{"CI005009.WI", "轻工制造"},
			{"CI005010.WI", "机械"},
			{"CI005011.WI", "电力设备"},
			{"CI005012.WI", "国防军工"},
			{"CI005013.WI", "汽车"},
			{"CI005014.WI", "商贸零售"},
			{"CI005015.WI", "餐饮旅游"},
			{"CI005016.WI", "家电"},
			{"CI005017.WI", "纺织服装"},
			{"CI005018.WI", "医药"},
			{"CI005019.WI", "食品饮料"},
			{"CI005020.WI", "农林牧渔"},
			{"CI005021.WI", "银行"},
			{"CI005022.WI", "非银行金融"},
			{"CI005023.WI", "房地产"},
			{"CI005024.WI", "交通运输"},
			{"CI005025.WI", "电子元器件"},
			{"CI005026.WI", "通信"},
			{"CI005027.WI", "计算机"},
			{"CI005028.WI", "传媒"},
			{"CI005029.WI", "综合"}
		});
		SI_INDUS_I = MapUtils.putAll(new ListOrderedMap<>(), new Object[][] {
			{"801010.SI", "农林牧渔"},
			{"801020.SI", "采掘"},
			{"801030.SI", "化工"},
			{"801040.SI", "钢铁"},
			{"801050.SI", "有色金属"},
			{"801080.SI", "电子"},
			{"801110.SI", "家用电器"},
			{"801120.SI", "食品饮料"},
			{"801130.SI", "纺织服装"},
			{"801140.SI", "轻工制造"},
			{"801150.SI", "医药生物"},
			{"801160.SI", "公用事业"},
			{"801170.SI", "交通运输"},
			{"801180.SI", "房地产"},
			{"801200.SI", "商业贸易"},
			{"801210.SI", "休闲服务"},
			{"801230.SI", "综合"},
			{"801710.SI", "建筑材料"},
			{"801720.SI", "建筑装饰"},
			{"801730.SI", "电气设备"},
			{"801740.SI", "国防军工"},
			{"801750.SI", "计算机"},
			{"801760.SI", "传媒"},
			{"801770.SI", "通信"},
			{"801780.SI", "银行"},
			{"801790.SI", "非因金融"},
			{"801880.SI", "汽车"},
			{"801890.SI", "机械设备"}
		});
		// @formatter:on
	}

	/**
	 * 
	 */
	private Constants() {

	}
}
